﻿using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace BattleTetrixReloaded
{
    public class tet_net_server
    {
        public class SocketPacket
        {
            public Socket m_currentSocket;
            public byte[] dataBuffer = new byte[(1024 * 1024 * 4) + 100];
        } 
        
        public int clientsIndex = 0;

        public Socket m_socListener;
        public Socket m_socWorker;       

        public tet_net_object netObject = null;

        public bool isConnected = false;

        Encoding encoding = Encoding.UTF8;        

        public AsyncCallback pfnWorkerCallback;
       

        public tet_net_server()
        {
            netObject = new tet_net_object();
            startListening();           
        }

        public void startListening()
        {            
            m_socListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, 8221);
            m_socListener.Bind(ipLocal);
            m_socListener.Listen(4);
            m_socListener.BeginAccept(new AsyncCallback(OnClientConnect), null);            
        }

        public void OnClientConnect(IAsyncResult asyn)
        {
            m_socWorker = m_socListener.EndAccept(asyn);
            WaitForData(); 
        }
        
        public void WaitForData()
        {            
            if (pfnWorkerCallback == null)                
                pfnWorkerCallback = new AsyncCallback(OnDataReceived);                
            SocketPacket theSokPkt = new SocketPacket();
            theSokPkt.m_currentSocket = m_socWorker;
            if (m_socWorker.Connected)
                m_socWorker.BeginReceive(theSokPkt.dataBuffer, 0, theSokPkt.dataBuffer.Length, SocketFlags.None, pfnWorkerCallback, theSokPkt);                       
        }

        public void OnDataReceived(IAsyncResult asyn)
        {
            SocketPacket socketData = (SocketPacket)asyn.AsyncState;

            int bytesReceived = 0;
            try
            {
                bytesReceived = socketData.m_currentSocket.EndReceive(asyn);
            }
            catch
            {
                tet_main.gameState = tet_constants.STATE_OP_DISC;
                return;
            }

            byte[] buffer = new byte[bytesReceived + 1];

            buffer = socketData.dataBuffer;

            IFormatter formatter = new BinaryFormatter();
            Stream stream = new MemoryStream();

            stream.Write(buffer, 0, buffer.Length);
            stream.Seek(0, 0);

            tet_net_object objReceived = (tet_net_object)formatter.Deserialize(stream);

            parseObject(objReceived);

            stream.Close();

            WaitForData();
           
        }

        private void parseObject(tet_net_object objReceived)
        {
            netObject = objReceived;
            if (netObject.isGameOver == true)
            {
                tet_main.gameState = tet_constants.STATE_VS_WON;
            } 
        }

        internal void sendMsgToClient(tet_net_object objToSend)
        {
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new MemoryStream();

            formatter.Serialize(stream, objToSend);

            byte[] buffer = ((MemoryStream)stream).ToArray();
            m_socWorker.Send(buffer, buffer.Length, 0);
        }


        public void Update(tet_game_loop gameLoop)
        {
            if (m_socWorker.Connected)
            {
                tet_net_object toSend = new tet_net_object();

                toSend.isReady = tet_main.isReady;

                if (gameLoop != null)
                {
                    toSend.ColorMatrix = gameLoop.colorMatrix;
                    toSend.ClearedLines = gameLoop.clearedLines;
                    toSend.Score = gameLoop.myScore.getScore();
                    toSend.UsedShapes = gameLoop.clearedLines;
                    toSend.NextShape = gameLoop.currentBlocks[gameLoop.usedShapesNr + 1];
                    toSend.isGameOver = gameLoop.isGameOver;
                }
               
                sendMsgToClient(toSend);
            }
            else
            {
                m_socWorker.Disconnect(true);
                tet_main.gameState = tet_constants.STATE_OP_DISC;
            }
        }
        public void Unload()
        {
            m_socListener.Close();
            //m_socWorker.Close();
        }
    }
}

